package db

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"monitor.ilin.cc/config"
	"monitor.ilin.cc/internal/models"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Service struct {
	db      *gorm.DB
	retrier *Retrier
	dsn     string
	// 用于缓存已经确认存在的表
	existingTables sync.Map
	// 用于批量写入的缓冲区
	tickerBuffer  map[string][]*models.Ticker
	bufferMutex   sync.RWMutex
	bufferSize    int
	flushInterval time.Duration
	lastFlushTime time.Time
}

type Retrier struct {
	maxAttempts int
	backoff     time.Duration
}

func NewRetrier(maxAttempts int, backoff time.Duration) *Retrier {
	return &Retrier{
		maxAttempts: maxAttempts,
		backoff:     backoff,
	}
}

func (r *Retrier) Do(operation func() error) error {
	var lastErr error
	for attempt := 0; attempt < r.maxAttempts; attempt++ {
		if err := operation(); err != nil {
			lastErr = err
			log.Printf("Operation failed (attempt %d/%d): %v", attempt+1, r.maxAttempts, err)
			if attempt < r.maxAttempts-1 {
				time.Sleep(r.backoff * time.Duration(attempt+1))
				continue
			}
		} else {
			return nil
		}
	}
	return fmt.Errorf("operation failed after %d attempts: %v", r.maxAttempts, lastErr)
}

func NewService(cfg *config.DatabaseConfig) (*Service, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		cfg.User,
		cfg.Password,
		cfg.Host,
		cfg.Port,
		cfg.DBName,
	)

	var db *gorm.DB
	var err error

	// 创建重试器
	retrier := NewRetrier(5, time.Second)
	err = retrier.Do(func() error {
		db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
			SkipDefaultTransaction: true,
			PrepareStmt:            true,
		})
		return err
	})

	if err != nil {
		return nil, fmt.Errorf("failed to connect database: %v", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		return nil, err
	}

	// 调整连接池参数
	sqlDB.SetMaxIdleConns(5)                   // 减少空闲连接数
	sqlDB.SetMaxOpenConns(20)                  // 减少最大连接数
	sqlDB.SetConnMaxLifetime(time.Hour)        // 保持连接生命周期
	sqlDB.SetConnMaxIdleTime(10 * time.Minute) // 设置空闲连接超时

	s := &Service{
		db:            db,
		dsn:           dsn,
		tickerBuffer:  make(map[string][]*models.Ticker),
		bufferSize:    1000,
		flushInterval: 2 * time.Second, // 每2秒刷新一次
		lastFlushTime: time.Now(),
		retrier:       retrier,
	}

	// 启动定期刷新和连接检查
	go s.periodicFlush()
	go s.checkConnection()

	return s, nil
}

// 添加数据库连接检查
func (s *Service) checkConnection() {
	ticker := time.NewTicker(30 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		sqlDB, err := s.db.DB()
		if err != nil {
			log.Printf("获取数据库连接失败: %v", err)
			continue
		}

		if err := sqlDB.Ping(); err != nil {
			log.Printf("数据库连接检查失败: %v", err)
			// 尝试重新连接
			if err := s.reconnect(); err != nil {
				log.Printf("数据库重连失败: %v", err)
			}
		}
	}
}

// 数据库重连
func (s *Service) reconnect() error {
	sqlDB, err := s.db.DB()
	if err != nil {
		return err
	}

	// 关闭现有连接
	sqlDB.Close()

	// 等待一段时间
	time.Sleep(5 * time.Second)

	// 重新连接
	return s.retrier.Do(func() error {
		newDB, err := gorm.Open(mysql.Open(s.dsn), &gorm.Config{
			SkipDefaultTransaction: true,
			PrepareStmt:            true,
		})
		if err != nil {
			return err
		}
		s.db = newDB
		return nil
	})
}

// 定期刷新缓冲区
func (s *Service) periodicFlush() {
	ticker := time.NewTicker(s.flushInterval)
	defer ticker.Stop()

	for range ticker.C {
		s.flushBuffer()
	}
}

func (s *Service) flushBuffer() {
	s.bufferMutex.Lock()
	defer s.bufferMutex.Unlock()

	for tableName, tickers := range s.tickerBuffer {
		if len(tickers) > 0 {
			if err := s.db.Table(tableName).CreateInBatches(tickers, 100).Error; err != nil {
				log.Printf("刷新表%s数据失败: %v", tableName, err)
			} else {
				//log.Printf("成功写入%d条数据到表%s", len(tickers), tableName)
			}
			s.tickerBuffer[tableName] = s.tickerBuffer[tableName][:0]
		}
	}
}

func formatInstID(instID string) string {
	lower := strings.ToLower(instID)
	lower = strings.Replace(lower, "-", "_", -1)
	return lower
}

func (s *Service) getTableName(ticker *models.Ticker) string {
	formattedInstID := formatInstID(ticker.InstID)
	return fmt.Sprintf("ticker_%s_%s",
		formattedInstID,
		ticker.CreatedAt.Format("200601"),
	)
}

func (s *Service) ensureTable(tableName string) error {
	if _, exists := s.existingTables.Load(tableName); exists {
		return nil
	}

	if !s.db.Migrator().HasTable(tableName) {
		err := s.db.Table(tableName).Migrator().CreateTable(&models.Ticker{})
		if err != nil {
			return fmt.Errorf("create table error: %v", err)
		}
	}

	s.existingTables.Store(tableName, true)
	return nil
}

func (s *Service) SaveTicker(ticker *models.Ticker) error {
	return s.retrier.Do(func() error {
		tableName := s.getTableName(ticker)

		if err := s.ensureTable(tableName); err != nil {
			return err
		}

		s.bufferMutex.Lock()
		defer s.bufferMutex.Unlock()

		s.tickerBuffer[tableName] = append(s.tickerBuffer[tableName], ticker)

		if len(s.tickerBuffer[tableName]) >= s.bufferSize {
			s.flushBuffer()
		}

		return nil
	})
}

func (s *Service) Close() error {
	s.flushBuffer()
	sqlDB, err := s.db.DB()
	if err != nil {
		return err
	}
	return sqlDB.Close()
}
